#include "statistics_tab.hpp"

#include "app/app_base.hpp"
#include "imgui/imgui.h"
#include "imgui/imgui_internal.h"
#include "imgui/implot.h"
#include "sql/types.hpp"
#include "data/app_config.hpp"
#include "fmt/format.h"

#include <mutex>
#include <shared_mutex>
#include <cmath>
#include <utility>
#include <algorithm>
#include <numeric>

#include "gui/ui_component.hpp"
#include "imgui/IconsFontAwesome4.h"
#include "imgui/implot_internal.h"
#include "utils/app_utils.hpp"
#include "export/xlsx_export.hpp"
#include "gui/controls/calendar.hpp"
#include <utils/date_utils.hpp>

struct StatisticsTabPrivateData
{
    std::string const name;
    std::string date_start;
    std::string date_end;
    std::function<void(std::string&, std::string&)> range_updater;

    bool is_first_show = true;
    StatisticsResult my_statistics_data{ .total_time = 0.0 };
    std::shared_mutex data_lck;
    std::atomic_bool reloading{ false };

    // 详情页的一些参数
    std::string tag_name;
    std::vector<WorkItem> tagged_items;

    // 高级统计
    double custom_total_time = 0.0;

    // 页面选中
    bool next_frame_select_tab = false;

    bool use_pie_chart = Value(Config::KEY_DEFAUT_PIE_CHART) == "yes";

    void Reload();
    void Render();
};

StatisticsTab::StatisticsTab(std::string name, std::function<void(std::string&, std::string&)> range_updater) :
    d{ new StatisticsTabPrivateData{.name = move(name), .range_updater = move(range_updater)} }
{
    assert(d->range_updater);
    d->range_updater(d->date_start, d->date_end);
}

StatisticsTab::StatisticsTab(std::string name) :
    d{ new StatisticsTabPrivateData{.name = move(name), .range_updater = nullptr} }
{
    d->date_start = Date::Current();
    d->date_end = Date::Current();
}

StatisticsTab::~StatisticsTab()
{
    delete d;
}

void StatisticsTab::Select()
{
    d->next_frame_select_tab = true;
}

void StatisticsTab::SetDate(const std::string &beg, const std::string &end)
{
    d->date_start = beg;
    d->date_end = end;
    d->is_first_show = true;
}

void StatisticsTab::Render() const
{
    using namespace ImGui;

    if (d->is_first_show)
    {
        d->is_first_show = false;
        d->Reload();
    }

    if (BeginTabItem(d->name.c_str(), nullptr, d->next_frame_select_tab ? ImGuiTabItemFlags_SetSelected : 0))
    {
        // clear select state
        if (d->next_frame_select_tab)
            d->next_frame_select_tab = false;

        d->Render();
        EndTabItem();
    }
}

void StatisticsTabPrivateData::Reload()
{
    App->Async([this]()
        {
            if (range_updater)
                range_updater(date_start, date_end);
            std::unique_lock lck(data_lck);
            my_statistics_data = App->Sql()->GetStatisticsResult(date_start, date_end);
            reloading = false;
        });
}

static void RenderTree(const double total_time, const StatisticsResult::TagTime& tag, int32_t* tag_l1, int32_t* tag_l2, std::string* tag_name)
{
    using namespace ImGui;

    TableNextRow();
    TableNextColumn();

    if (!tag.sub_tags.empty())
    {
        const bool open = TreeNodeEx(tag.tag_name.c_str(),
            ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_AllowOverlap);
        TableNextColumn();
        Text("%7.2f小时（%.4f天）", tag.tag_time, tag.tag_time / 8.0);
        TableNextColumn();
        Text("%8.4f%%", 100.0 * tag.tag_time / total_time);
        TableNextColumn();
        if (SmallButton(ICON_FA_LIST " 详情"))
        {
            if (tag_l1)
                *tag_l1 = tag.tag_id;
            if (tag_name)
                *tag_name = tag.tag_name;
        }

        if (open)
        {
            double sum = 0.0;
            for (auto& x : tag.sub_tags)
            {
                sum += x.tag_time;
                PushID(&x);
                RenderTree(total_time, x, tag_l2, nullptr, tag_name);
                if (tag_l2 && *tag_l2 != 0)
                {
                    if (tag_l1 && *tag_l1 == 0)
                        *tag_l1 = tag.tag_id;
                }
                PopID();
            }
            if (sum < tag.tag_time)
            {
                PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 0, 255));
                TableNextRow();
                TableNextColumn();
                Bullet();
                SameLine();
                Selectable("未分类", false,
                    ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
                TableNextColumn();
                Text("%7.2f小时（%.4f天）", tag.tag_time - sum, (tag.tag_time - sum) / 8.0);
                TableNextColumn();
                Text("%8.4f%%", (tag.tag_time - sum) / total_time * 100.0);
                TableNextColumn();
                PopStyleColor(1);
            }
            TreePop();
        }
    }
    else
    {
        Bullet();
        SameLine();
        Selectable(tag.tag_name.c_str(), false,
            ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
        TableNextColumn();
        Text("%7.2f小时（%.4f天）", tag.tag_time, tag.tag_time / 8.0);
        TableNextColumn();
        Text("%8.4f%%", 100.0 * tag.tag_time / total_time);
        TableNextColumn();
        if (SmallButton(ICON_FA_LIST " 详情"))
        {
            if (tag_l1)
                *tag_l1 = tag.tag_id;
            if (tag_name)
                *tag_name = tag.tag_name;
        }
    }
}

static constexpr const char* recalc_text = ICON_FA_REFRESH " 重新计算";
static constexpr const char* export_text = ICON_FA_SIGN_OUT " 导出为表格";
static constexpr const char* pie_chart_text = ICON_FA_PIE_CHART " 饼图";
static constexpr const char* bar_chart_text = ICON_FA_BAR_CHART " 柱状图";

static float Modulo(const ImVec2& vec)
{
    return sqrt(vec.x * vec.x + vec.y * vec.y);
}

void StatisticsTabPrivateData::Render()
{
    using namespace ImGui;

    AlignTextToFramePadding();

    const char* switch_text = use_pie_chart ? bar_chart_text : pie_chart_text;

    BulletText(ICON_FA_CALENDAR " %s 至 %s", date_start.data(), date_end.data());

    float const max_x = (ImGui::GetContentRegionAvail() + ImGui::GetCursorScreenPos() - ImGui::GetWindowPos()).x;

    if (!range_updater) {
        // this is custom tab, only one
        static DateEditor start_editor("##DateStart");
        static DateEditor end_editor("##DateEnd");

        if (start_editor.RenderAsPopup(false)) {
            date_start = start_editor.GetSelectedDate();
        }
        if (end_editor.RenderAsPopup(false)) {
            date_end = end_editor.GetSelectedDate();
        }

        SameLine();
        if (Button(ICON_FA_CALENDAR " 选择开始时间")) start_editor.Show();
        SameLine();
        if (Button(ICON_FA_CALENDAR " 选择结束时间")) end_editor.Show();

        SameLine();

        static std::string quick_s, quick_e;

        if (Button(ICON_FA_ROCKET " 快速选择")) {
            quick_s = start_editor.GetSelectedDate();
            quick_e = end_editor.GetSelectedDate();
            OpenPopup("快速选择菜单");
        }

        if (BeginPopup("快速选择菜单"))
        {
            AlignTextToFramePadding();
            if (CollapsingHeader("当前日期", ImGuiTreeNodeFlags_DefaultOpen))
            {
                BulletText("开始时间：%s", quick_s.c_str());
                BulletText("结束时间：%s", quick_e.c_str());
            }
            if (CollapsingHeader("快速定位", ImGuiTreeNodeFlags_DefaultOpen))
            {
                if (Button(ICON_FA_ARROW_LEFT " 上一年")) {
                    AdjustDateRange(quick_s, quick_e, Granularity::Year, AdjustType::Previous);
                }
                SameLine();
                if (Button(ICON_FA_ARROW_DOWN " 本年度")) {
                    AdjustDateRange(quick_s, quick_e, Granularity::Year, AdjustType::Current);
                }
                SameLine();
                if (Button(ICON_FA_ARROW_RIGHT " 下一年")) {
                    AdjustDateRange(quick_s, quick_e, Granularity::Year, AdjustType::Next);
                }

                if (Button(ICON_FA_ARROW_LEFT " 上一季度")) {
                    AdjustDateRange(quick_s, quick_e, Granularity::Quarter, AdjustType::Previous);
                }
                SameLine();
                if (Button(ICON_FA_ARROW_DOWN " 本季度")) {
                    AdjustDateRange(quick_s, quick_e, Granularity::Quarter, AdjustType::Current);
                }
                SameLine();
                if (Button(ICON_FA_ARROW_RIGHT " 下一季度")) {
                    AdjustDateRange(quick_s, quick_e, Granularity::Quarter, AdjustType::Next);
                }

                if (Button(ICON_FA_ARROW_LEFT " 上月")) {
                    AdjustDateRange(quick_s, quick_e, Granularity::Month, AdjustType::Previous);
                }
                SameLine();
                if (Button(ICON_FA_ARROW_DOWN " 本月")) {
                    AdjustDateRange(quick_s, quick_e, Granularity::Month, AdjustType::Current);
                }
                SameLine();
                if (Button(ICON_FA_ARROW_RIGHT " 下月")) {
                    AdjustDateRange(quick_s, quick_e, Granularity::Month, AdjustType::Next);
                }

                if (Button(ICON_FA_ARROW_LEFT " 上一周")) {
                    AdjustDateRange(quick_s, quick_e, Granularity::Week, AdjustType::Previous);
                }
                SameLine();
                if (Button(ICON_FA_ARROW_DOWN " 本周")) {
                    AdjustDateRange(quick_s, quick_e, Granularity::Week, AdjustType::Current);
                }
                SameLine();
                if (Button(ICON_FA_ARROW_RIGHT " 下一周")) {
                    AdjustDateRange(quick_s, quick_e, Granularity::Week, AdjustType::Next);
                }
            }

            SeparatorText("注意");
            TextUnformatted("上... 是以开始日期为基准");
            TextUnformatted("本... 是以开始日期为基准");
            TextUnformatted("下... 是以结束日期为基准");

            if (Button(ICON_FA_CHECK_SQUARE_O " 确定")) {
                date_start = quick_s;
                date_end = quick_e;
                start_editor.SetData(Date::Parse(quick_s));
                end_editor.SetData(Date::Parse(quick_e));
                CloseCurrentPopup();
                Reload();
            }
            EndPopup();
        }
    }

    BeginDisabled(reloading);
    SameLine(
        max_x - CalcTextSize(recalc_text).x - GetFontSize() - CalcTextSize(export_text).x -
        GetFontSize() - CalcTextSize(switch_text).x - GetFontSize());
    if (Button(switch_text))
    {
        use_pie_chart = !use_pie_chart;
    }
    SameLine(
        max_x - CalcTextSize(export_text).x - GetFontSize() - CalcTextSize(recalc_text).x -
        GetFontSize());
    if (Button(export_text))
    {
        App->Async([&]
            {
                ShowProgressDialog(true);
                auto const path = Export::ExportToXls(date_start, date_end, my_statistics_data, custom_total_time);
                ShowProgressDialog(false);
                if (path.empty())
                {
                    SendError("导出失败！！");
                }
                else
                {
                    SendInfo(fmt::format("导出成功，目标文件：\n{}", path));
                }
            });
    }
    SameLine(max_x - CalcTextSize(recalc_text).x - GetFontSize());
    if (Button(recalc_text))
    {
        reloading = true;
        Reload();
        SendNotification("重新统计完成！");
    }
    Separator();
    AlignTextToFramePadding();
    if (custom_total_time == 0.0) {
        BulletText("总记录工时：%.2f 小时", my_statistics_data.total_time);
    }
    else
    {
        BulletText("总记录工时：%.2f / 基准时间：%.2f 小时", my_statistics_data.total_time, custom_total_time);
        HelperMaker("基准时间影响表格统计的百分比数值（总数可能会不足100%或者超过100%）", true);
        SameLine();
        if (Button(ICON_FA_TIMES " 清除参数"))
            custom_total_time = 0.0;
    }
    SameLine();

    // 自定义统计参数
    static double temp_data;
    if (Button(ICON_FA_COG " 参数设置"))
    {
        temp_data = custom_total_time;
        OpenPopup("StatisticsParams");
    }

    if (BeginPopup("StatisticsParams"))
    {
        AlignTextToFramePadding();
        TextUnformatted("基准工时：");
        SameLine();
        SetNextItemWidth(120);
        InputDouble("##自定义基准工时", &temp_data, 0, 0, "%.2f");
        SameLine();
        if (Button(ICON_FA_CHECK " 确定"))
        {
            custom_total_time = temp_data;
            CloseCurrentPopup();
        }
        EndPopup();
    }

    Spacing();

    const float chart_max_width = std::max<float>(500.f, max_x * 0.3f);

    if (!use_pie_chart)
    {
        // 使用柱状图
        if (ImPlot::BeginPlot("工时分布统计信息##Bar", ImVec2(chart_max_width, -1),
            ImPlotFlags_NoMenus | ImPlotFlags_NoMouseText |
            ImPlotFlags_NoBoxSelect | ImPlotFlags_NoFrame | ImPlotFlags_NoLegend))
        {
            ImPlot::SetupAxes("标签", "耗时(小时)", ImPlotAxisFlags_NoDecorations);
            ImPlot::SetupAxesLimits(-0.6, static_cast<double>(my_statistics_data.tags.size()) - 0.4, 0,
                std::max<double>(1.2 * std::max(my_statistics_data.total_time, custom_total_time), 10.0), ImPlotCond_Always);
            ImPlot::SetupAxisFormat(ImAxis_X1, [](double p, char* buff, int size, void* ud)
                {
                    if (fabs(p - floor(p)) <= 1e-6)
                    {
                        const auto* d = static_cast<StatisticsTabPrivateData*>(ud);
                        if (const int idx = static_cast<int>(p); idx < static_cast<int>(d->my_statistics_data.tags.size())
                            && 0
                            >= idx)
                            return snprintf(buff, size, "%s", d->my_statistics_data.tags[static_cast<int>(p)].tag_name.c_str());
                        else {
                            buff[0] = 0;
                            return 0;
                        }
                    }
                    else {
                        buff[0] = 0;
                        return 0;
                    }
                }, this);
            ImPlot::PlotBarsG("标签耗时数据", [](int idx, void *ud)
                {
                    const auto* d = static_cast<StatisticsTabPrivateData*>(ud);
                    return ImPlotPoint{ static_cast<double>(idx), d->my_statistics_data.tags[idx].tag_time };
                }, this, static_cast<int>(my_statistics_data.tags.size()), 0.5);
            if (ImPlot::IsPlotHovered())
            {
                ImDrawList* draw_list = ImPlot::GetPlotDrawList();
                ImPlotPoint mouse = ImPlot::GetPlotMousePos();
                mouse.x = ImPlot::RoundTo(mouse.x, 0);

                const float tool_l = ImPlot::PlotToPixels(mouse.x - 0.25 * 1.5, mouse.y).x;
                const float tool_r = ImPlot::PlotToPixels(mouse.x + 0.25 * 1.5, mouse.y).x;
                const float tool_t = ImPlot::GetPlotPos().y;
                const float tool_b = tool_t + ImPlot::GetPlotSize().y;
                ImPlot::PushPlotClipRect();
                draw_list->AddRectFilled(ImVec2(tool_l, tool_t), ImVec2(tool_r, tool_b), IM_COL32(128, 128, 128, 64));
                ImPlot::PopPlotClipRect();

                const double total_time = custom_total_time == 0.0 ? my_statistics_data.total_time : custom_total_time;

                if (mouse.x >= 0 && mouse.x < static_cast<double>(my_statistics_data.tags.size()))
                {
                    BeginTooltip();
                    BulletText("%s",
                        my_statistics_data.tags[static_cast<int>(mouse.x)].tag_name.data()
                    );
                    Separator();
                    Text("%.2f小时/占比%.4f%%",
                        my_statistics_data.tags[static_cast<int>(mouse.x)].tag_time,
                        my_statistics_data.tags[static_cast<int>(mouse.x)].tag_time / total_time *
                        100);
                    if (const auto& root_tag = my_statistics_data.tags[static_cast<int>(mouse.x)]; !root_tag.sub_tags.
                        empty())
                    {
                        Separator();
                        for (const auto& [t_id, t_name, t_time, tags] : root_tag.sub_tags)
                        {
                            Text("%s（%.2f小时/占比%.4f%%）", t_name.c_str(), t_time,
                                t_time / total_time * 100.0);
                        }
                    }
                    EndTooltip();
                }
            }
            ImPlot::TagY(my_statistics_data.total_time, ImColor(IM_COL32(255, 255, 0, 255)));
            ImPlot::PlotInfLines("##TOTAL_TIME", &my_statistics_data.total_time, 1, ImPlotInfLinesFlags_Horizontal);
            if (custom_total_time != 0.0)
            {
                ImPlot::TagY(custom_total_time, ImColor(IM_COL32(255, 0, 255, 255)));
                ImPlot::PlotInfLines("##BASE_TIME", &custom_total_time, 1, ImPlotInfLinesFlags_Horizontal);
            }
            ImPlot::EndPlot();
        }
    }
    else // 使用饼图
    {
        // ImPlot::PushColormap(ImPlotColormap_Paired);
        if (ImPlot::BeginPlot("工时分布统计信息##Pie", ImVec2(chart_max_width, -1),
            ImPlotFlags_NoMenus | ImPlotFlags_NoMouseText |
            ImPlotFlags_NoBoxSelect | ImPlotFlags_Equal)) {

            // pie chart parameters
            constexpr double radius = 0.4;
            constexpr double start_angle = 180;
            constexpr double center_x = 0.5;
            constexpr double center_y = 0.5;

            ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations);
            ImPlot::SetupAxisLimits(ImAxis_X1, 0, 1, ImPlotCond_Always);
            ImPlot::SetupLegend(ImPlotLocation_South, ImPlotLegendFlags_Outside | ImPlotLegendFlags_Horizontal | ImPlotLegendFlags_NoButtons);

            std::vector<const char*> names(my_statistics_data.tags.size());
            std::vector<double> values(my_statistics_data.tags.size());
            std::ranges::transform(my_statistics_data.tags, begin(names),
                [](const StatisticsResult::TagTime& t) { return t.tag_name.c_str(); });
            std::ranges::transform(my_statistics_data.tags, begin(values),
                [](const StatisticsResult::TagTime& t) { return t.tag_time; });
            ImPlot::PlotPieChart(names.data(), values.data(), static_cast<int>(my_statistics_data.tags.size()), center_x, center_y, radius, "", start_angle, ImPlotPieChartFlags_Normalize);
            if (ImPlot::IsPlotHovered())
            {
                ImPlotPoint p = ImPlot::GetPlotMousePos();
                if (sqrt((p.x - center_x) * (p.x - center_x) + (p.y - center_y) * (p.y - center_y)) < radius)// inside pie chart
                {
                    // 计算夹角，确定是哪个Tag
                    const double total_v = std::accumulate(begin(values), end(values), 0.0);
                    double a0 = 0;

                    // 基准向量,
                    const ImVec2 vec_base((float)(radius * cos(start_angle * 2 * IM_PI / 360.0)), (float)(radius * sin(start_angle * 2 * IM_PI / 360.0)));
                    const ImVec2 vec_point((float)(p.x - center_x), (float)(p.y - center_y));

                    // 向量夹角公式 θ = acos(v1·v2/|v1||v2|)
                    double mouse_angle = atan2(vec_point.y, vec_point.x) - atan2(vec_base.y, vec_base.x);
                    if (mouse_angle < 0)
                        mouse_angle += (2.0 * IM_PI);

                    uint32_t idx = 0;
                    for (; idx < values.size(); ++idx)
                    {
                        const double percent = values[idx] / total_v;
                        const double a1 = a0 + 2.0 * IM_PI * percent;

                        if (a0 <= mouse_angle && mouse_angle <= a1)
                        {
                            break;
                        }
                        a0 = a1;
                    }

                    if (idx < values.size())
                    {
                        const double total_time = custom_total_time == 0.0 ? my_statistics_data.total_time : custom_total_time;

                        BeginTooltip();
                        BulletText("%s",
                            my_statistics_data.tags[idx].tag_name.data()
                        );
                        Separator();
                        Text("%.2f小时/占比%.4f%%",
                            my_statistics_data.tags[idx].tag_time,
                            my_statistics_data.tags[idx].tag_time / total_time *
                            100);
                        if (const auto& root_tag = my_statistics_data.tags[idx]; !root_tag.sub_tags.
                            empty())
                        {
                            Separator();
                            for (const auto& [t_id, t_name, t_time, t_tags] : root_tag.sub_tags)
                            {
                                Text("%s（%.2f小时/占比%.4f%%）", t_name.c_str(), t_time,
                                    t_time / total_time * 100.0);
                            }
                        }
                        EndTooltip();
                    }
                }
            }
            ImPlot::EndPlot();
        }

        // ImPlot::PopColormap();
    }

    SameLine();

    // 用于检测是否按下详情按钮
    int32_t tag_l1 = 0;
    int32_t tag_l2 = 0;
    std::string active_tag_name;

    if (BeginTable("详细信息统计表", 4,
        ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit, ImVec2(-1, -1)))
    {
        TableSetupColumn("标签", ImGuiTableColumnFlags_WidthStretch, 350);
        TableSetupColumn("耗时");
        TableSetupColumn("占比");
        TableSetupColumn("操作");
        TableHeadersRow();

        int n = 0;
        double total_time = my_statistics_data.total_time;
        if (custom_total_time != 0.0)
            total_time = custom_total_time;
        double sum_of_time = 0.0;
        for (auto const& x : my_statistics_data.tags)
        {
            PushID(n++);
            RenderTree(total_time, x, &tag_l1, &tag_l2, &active_tag_name);
            PopID();
            sum_of_time += x.tag_time; // 计算一级tag的时间和
        }

        if (sum_of_time < my_statistics_data.total_time)
        {
            const auto diff = my_statistics_data.total_time - sum_of_time;
            // 这里有未分类的项
            PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 0, 255));
            TableNextRow();
            TableNextColumn();
            BulletText("无标签");
            TableNextColumn();
            Text("%7.2f小时（%.4f天）", diff, diff / 8.0);
            TableNextColumn();
            Text("%8.4f%%", diff / total_time * 100.0);
            PopStyleColor(1);
        }

        // 统计列
        TableNextRow();
        TableNextColumn();
        BulletText("统计信息");
        TableNextColumn();
        Text("%7.2f小时（%.4f天）", my_statistics_data.total_time, my_statistics_data.total_time / 8.0);
        TableNextColumn();
        Text("%8.4f%%", my_statistics_data.total_time / total_time * 100.0);

        EndTable();
    }

    if (tag_l1 != 0)
    {
        tag_name = active_tag_name;
        tagged_items = App->Sql()->GetItemsByTagAndDate(date_start, date_end, tag_l1, tag_l2);
        OpenPopup("标签详细数据");
    }

    if (BeginPopup("标签详细数据"))
    {
        for (auto const& item : tagged_items)
        {
            PushID(&item);
            BulletText("[%s] %s（%.2f小时）", item.create_date.c_str(), item.comment.c_str(), item.hour);
            PopID();
        }
        EndPopup();
    }

    EndDisabled();
}
